package it.ap.sa.core.configuration.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import it.ap.sa.core.configuration.commons.ConfigurationUtils;
import it.ap.sa.core.configuration.commons.IConfiguration;
import it.ap.sa.core.configuration.commons.MemConfig;
import it.ap.sa.core.configuration.iservice.IService;
import it.ap.sa.core.configuration.reader.IConfigurationReader;
import it.ap.sa.core.confservice.event.ConfigurationEventReload;
import it.ap.sa.core.confservice.event.CoreServiceEventReload;
import it.ap.sa.core.utils.SpringContextProvider;
import it.ap.sa.core.utils.costants.ConfigurationConstants;

/**
 * @author Alessandro Paolillo
 */
@Service
public class ConfigurationService implements IService {
	
	/** Oggetto contenente i parametri di configurazione **/
	private static IConfiguration config = new MemConfig();

	private ConfigurationService() {}

	@Autowired
	ApplicationEventPublisher publisher;

	private static final Logger logger = LoggerFactory.getLogger(ConfigurationService.class.getName());

	public static IConfiguration getConfig() {
		return ConfigurationService.config;
	}

	/**
	 * Funzione che determina il reader da usare in base al parametro di configurazione "configReader"
	 * presente nell'oggetto {@link IConfiguration}.
	 * 
	 * @return {@link IConfiguration}
	 */
	public static IConfiguration buildConfiguration() {
		try {
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Building reader ... IN CORSO <<<<<<<<<<<<<<<<< ");
			
			/**
			 * Recupero dall'oggetto {@link IConfiguration} la classe writer impostata
			 */
			String readerClass = ConfigurationService.getConfig().getString(ConfigurationConstants.CONFIG_READER.name());
			
			if (!StringUtils.hasText(readerClass)){
				logger.error(">>>>>>>>>>>>>>>> Nessun reader definito <<<<<<<<<<<<<<<");
				
				throw new RuntimeException("Reader non inizializzato (reader is null)");
			}
			
			logger.info("Build configuration with reader: " + readerClass);
			
			IConfigurationReader confreader = null;
			
			try{
				Class<?> clazz = Class.forName(readerClass);
				
				confreader = (IConfigurationReader) SpringContextProvider.getApplicationContext().getBean(clazz);
			}catch(BeansException e){
				logger.error("La classe definita come reader non è stata trovata " + e, e);
				
				throw new RuntimeException(">>>>>>>>>>>>>>>>  No class reader Found! <<<<<<<<<<<<<<< ", e);
			}
				
			/**
			 * Leggo i parametri di configurazione a partire dal reader appena determinato..
			 */
			confreader.readConfiguration(null);

			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Building reader ... TERMINATO <<<<<<<<<<<<<<<<< ");

			return config;
		} catch (Exception ex) {
			logger.error(">>>>>>>>>>>>>>>>  No configuration reader Found! <<<<<<<<<<<<<<< ", ex);
			
			throw new RuntimeException(">>>>>>>>>>>>>>>>  No configuration reader Found! <<<<<<<<<<<<<<< ", ex);
		}
	}

	/**
	 * @author Alessandro Paolillo
	 */
	@EventListener
	public void onReloadConfigEvent(ConfigurationEventReload event) {
		try{
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Reload Configuration service ... IN CORSO <<<<<<<<<<<<<<<<< ");
				
			/**
			 * Svuoto l'oggetto configuration
			 */
			ConfigurationService.getConfig().clear();
			
			logger.info("Sono stati cancellati da configuration tutti i parametri. ");
			
			/**
			 * Ricarico i parametri di configurazione
			 */
			ConfigurationService.buildConfiguration();
		
			logger.info("Sono stati ricaricati i seguenti parametri di configurazione: ");
			logger.info(ConfigurationUtils.toString(ConfigurationService.getConfig()));
			
			/** 
			 * Pubblico l'evento che permette a tutti i servizi di riconfigurarsi a seguito del fatto che
			 * sono cambiati i parametri di configurazione.
			 */
			publisher.publishEvent(new CoreServiceEventReload(this, event.getReloadService()));
				
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Configuration restarted... TERMINATO <<<<<<<<<<<<<<<<< ");
		}catch(Exception e){
			logger.error("Error reload configuration service" + e, e);
			
			throw new RuntimeException(">>>>>>>>>>>>>>>> Reload configuration event error! <<<<<<<<<<<<<<< ", e);
		}
	}
}
